package com.cold.legendary.bprocess.trigger.transaction;

import com.alibaba.fastjson.JSON;
import com.cold.legendary.bprocess.common.constance.BErrorCodeEnum;
import com.cold.legendary.bprocess.common.constance.CommonConstances;
import com.cold.legendary.bprocess.common.exception.BProcessException;
import com.cold.legendary.bprocess.context.ins.BPointIns;
import com.cold.legendary.bprocess.context.run.BContext;
import com.cold.legendary.bprocess.db.enums.BizStatusEnum;
import com.cold.legendary.bprocess.db.enums.PointRunStatusEnum;
import com.cold.legendary.bprocess.db.model.TbProcessRequest;
import com.cold.legendary.bprocess.db.repository.ProcessRequestLogRepository;
import com.cold.legendary.bprocess.db.repository.ProcessRequestRepository;
import com.cold.legendary.bprocess.db.repository.dto.ReqLogInitDto;
import com.cold.legendary.bprocess.trigger.helper.ProcessRequestHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * @author cold
 * @since 2023/3/10
 */
@Slf4j
@Service
public class BProcessTransactionService {

    @Resource
    private ProcessRequestRepository processRequestRepository;
    @Resource
    private ProcessRequestLogRepository processRequestLogRepository;

    /**
     * 初始化流程请求信息
     *
     * @param bContext 上下文
     * @param newBPointIns 新节点实例
     * @return 流程请求id
     */
    @Transactional
    public String initPoint(BContext bContext, BPointIns newBPointIns) {
        //初始化流水请求表记录
        TbProcessRequest initProcessRequest = ProcessRequestHelper.buildInitTbProcessRequest(bContext.getBizId(), bContext.getMerchantId(), bContext.getProcessId(), bContext);
        String requestId = processRequestRepository.initTbProcessRequest(initProcessRequest);

        TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);

        String oldPointRunStatus = null;
        String oldPointId = null;
        String oldPointName = null;
        String oldBContext = null;

        String newPointRunStatus = PointRunStatusEnum.PROCESSING.getCode();
        String newPointId = newBPointIns.getPointId();
        String newPointName = newBPointIns.getPointName();
        String newBContext = JSON.toJSONString(bContext);

        processRequestLogRepository.initTbProcessRequestLog(
                ReqLogInitDto.builder()
                        .qryProcessRequest(qryProcessRequest)
                        .oldPointRunStatus(oldPointRunStatus)
                        .newPointRunStatus(newPointRunStatus)
                        .oldPointId(oldPointId)
                        .newPointId(newPointId)
                        .oldPointName(oldPointName)
                        .newPointName(newPointName)
                        .oldBContext(oldBContext)
                        .newBContext(newBContext)
                        .build());
        return requestId;
    }

    /**
     * 继续进行节点数据变更
     *
     * @param bContext 上下文
     * @param curPointId 当前节点id
     * @param triggerBPointIns 触发节点实例
     */
    @Transactional
    public void continuePoint(BContext bContext, String curPointId, BPointIns triggerBPointIns) {

        String requestId = bContext.getRequestId();

        TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);

        TbProcessRequest updateProcessRequest = ProcessRequestHelper.buildUpdateForContinue(qryProcessRequest, triggerBPointIns, bContext);
        processRequestRepository.updateForContinue(updateProcessRequest, curPointId);

        String oldPointRunStatus = qryProcessRequest.getPointRunStatus();
        String oldBizStatus = qryProcessRequest.getBizStatus();
        String oldPointId = qryProcessRequest.getPointId();
        String oldPointName = qryProcessRequest.getPointName();
        String oldBContext = qryProcessRequest.getRunContext();

        String newPointRunStatus = PointRunStatusEnum.PROCESSING.getCode();
        String newPointId = triggerBPointIns.getPointId();
        String newPointName = triggerBPointIns.getPointName();
        String newBContext = JSON.toJSONString(bContext);
        String newBizStatus = bContext.getBizStatusEnum().getCode();

        processRequestLogRepository.initTbProcessRequestLog(
                ReqLogInitDto.builder()
                        .qryProcessRequest(qryProcessRequest)
                        .oldPointRunStatus(oldPointRunStatus)
                        .newPointRunStatus(newPointRunStatus)
                        .oldBizStatus(oldBizStatus)
                        .newBizStatus(newBizStatus)
                        .oldPointId(oldPointId)
                        .newPointId(newPointId)
                        .oldPointName(oldPointName)
                        .newPointName(newPointName)
                        .oldBContext(oldBContext)
                        .newBContext(newBContext)
                        .build());
    }


    /**
     * 流转节点信息
     * @param bContext 上下文
     * @param curPointId  当前节点id
     * @param triggerBPointIns 触发节点实例
     */
    @Transactional
    public void roamPoint(BContext bContext, String curPointId, BPointIns triggerBPointIns) {

        String requestId = bContext.getRequestId();

        TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);

        TbProcessRequest updateProcessRequest = ProcessRequestHelper.buildUpdateTbProcessRequest(triggerBPointIns, bContext);
        processRequestRepository.updateTbProcessRequest(qryProcessRequest.getRequestId(), curPointId, updateProcessRequest);


        String oldPointRunStatus = qryProcessRequest.getPointRunStatus();
        String oldBizStatus = qryProcessRequest.getBizStatus();
        String oldPointId = qryProcessRequest.getPointId();
        String oldPointName = qryProcessRequest.getPointName();
        String oldBContext = qryProcessRequest.getRunContext();

        String newPointRunStatus = qryProcessRequest.getPointRunStatus();
        String newBizStatus = bContext.getBizStatusEnum().getCode();
        String newPointId = triggerBPointIns.getPointId();
        String newPointName = triggerBPointIns.getPointName();
        String newBContext = JSON.toJSONString(bContext);

        processRequestLogRepository.initTbProcessRequestLog(
                ReqLogInitDto.builder()
                        .qryProcessRequest(qryProcessRequest)
                        .oldPointRunStatus(oldPointRunStatus)
                        .newPointRunStatus(newPointRunStatus)
                        .oldBizStatus(oldBizStatus)
                        .newBizStatus(newBizStatus)
                        .oldPointId(oldPointId)
                        .newPointId(newPointId)
                        .oldPointName(oldPointName)
                        .newPointName(newPointName)
                        .oldBContext(oldBContext)
                        .newBContext(newBContext)
                        .build());
    }

    /**
     * 等待节点
     * @param bContext 上下文
     * @param curPointId 当前节点id
     */
    @Transactional
    public void waitPoint(BContext bContext, String curPointId) {

        String requestId = bContext.getRequestId();

        TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);

        TbProcessRequest updateProcessRequest = new TbProcessRequest();
        updateProcessRequest.setRequestId(qryProcessRequest.getRequestId());
        updateProcessRequest.setPointRunMsg("节点等待=" + curPointId);
        updateProcessRequest.setRunContext(JSON.toJSONString(bContext));
        updateProcessRequest.setBizRefId(bContext.getBizRefId());
        if (null != bContext.getBizStatusEnum()) {
            updateProcessRequest.setBizStatus(bContext.getBizStatusEnum().getCode());
        }
        processRequestRepository.updateToWaiting(updateProcessRequest, curPointId);


        String oldPointRunStatus = qryProcessRequest.getPointRunStatus();
        String oldBizStatus = qryProcessRequest.getBizStatus();
        String oldPointId = qryProcessRequest.getPointId();
        String oldPointName = qryProcessRequest.getPointName();
        String oldBContext = qryProcessRequest.getRunContext();

        String newPointRunStatus = PointRunStatusEnum.WAITING.getCode();
        String newBizStatus = bContext.getBizStatusEnum().getCode();
        String newPointId = qryProcessRequest.getPointId();
        String newPointName = qryProcessRequest.getPointName();
        String newBContext = JSON.toJSONString(bContext);

        processRequestLogRepository.initTbProcessRequestLog(
                ReqLogInitDto.builder()
                        .qryProcessRequest(qryProcessRequest)
                        .oldPointRunStatus(oldPointRunStatus)
                        .newPointRunStatus(newPointRunStatus)
                        .oldBizStatus(oldBizStatus)
                        .newBizStatus(newBizStatus)
                        .oldPointId(oldPointId)
                        .newPointId(newPointId)
                        .oldPointName(oldPointName)
                        .newPointName(newPointName)
                        .oldBContext(oldBContext)
                        .newBContext(newBContext)
                        .build());
    }


    /**
     * 结束节点信息
     *
     * @param bContext 上下文
     * @param curPointId 当前节点id
     */
    @Transactional
    public void finishPoint(BContext bContext, String curPointId) {
        if (null == bContext.getBizStatusEnum() || BizStatusEnum.PROCESSING.equals(bContext.getBizStatusEnum())) {
            bContext.setBizStatusEnum(BizStatusEnum.SUCCESS);
        }
        doFinishPoint(bContext, curPointId, null);
    }

    private void doFinishPoint(BContext bContext, String curPointId, String remark) {
        String requestId = bContext.getRequestId();
        BizStatusEnum bizStatusEnum = bContext.getBizStatusEnum();

        TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);
        processRequestRepository.updateToFinish(qryProcessRequest.getRequestId(), curPointId, bizStatusEnum, remark, bContext);

        String oldPointRunStatus = qryProcessRequest.getPointRunStatus();
        String oldBizStatus = qryProcessRequest.getBizStatus();
        String oldPointId = qryProcessRequest.getPointId();
        String oldPointName = qryProcessRequest.getPointName();
        String oldBContext = qryProcessRequest.getRunContext();

        String newPointRunStatus = PointRunStatusEnum.FINISH.getCode();
        String newPointId = qryProcessRequest.getPointId();
        String newPointName = qryProcessRequest.getPointName();
        String newBContext = JSON.toJSONString(bContext);
        String newBizStatus = qryProcessRequest.getBizStatus();
        ;
        if (null != bContext.getBizStatusEnum()) {
            newBizStatus = bContext.getBizStatusEnum().getCode();
        }

        processRequestLogRepository.initTbProcessRequestLog(
                ReqLogInitDto.builder()
                        .qryProcessRequest(qryProcessRequest)
                        .oldPointRunStatus(oldPointRunStatus)
                        .newPointRunStatus(newPointRunStatus)
                        .oldBizStatus(oldBizStatus)
                        .newBizStatus(newBizStatus)
                        .oldPointId(oldPointId)
                        .newPointId(newPointId)
                        .oldPointName(oldPointName)
                        .newPointName(newPointName)
                        .oldBContext(oldBContext)
                        .newBContext(newBContext)
                        .build());
    }


    /**
     * 异常节点
     * @param bContext 上下文
     * @param e 异常
     */
    @Transactional
    public void errorPoint(BContext bContext, Exception e) {
        String requestId = bContext.getRequestId();

        TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);
        if (null == qryProcessRequest) {
            log.error("流程请求记录不存在,无法进行节点自动重试!", e);
            throw new BProcessException(BErrorCodeEnum.SYS_PROCESS_REQUEST_NONE_ERROR, e);
        }

        if (PointRunStatusEnum.ERROR.getCode().equals(qryProcessRequest.getPointRunStatus())) {
            log.info("已是error状态!requestId={}", requestId);
            return;
        }

        processRequestRepository.updateToError(qryProcessRequest.getRequestId());

        String oldPointRunStatus = qryProcessRequest.getPointRunStatus();
        String oldPointId = qryProcessRequest.getPointId();
        String oldPointName = qryProcessRequest.getPointName();
        String oldBContext = qryProcessRequest.getRunContext();

        String newPointRunStatus = PointRunStatusEnum.ERROR.getCode();
        String newPointId = qryProcessRequest.getPointId();
        String newPointName = qryProcessRequest.getPointName();
        String newBContext = qryProcessRequest.getRunContext();

        processRequestLogRepository.initTbProcessRequestLog(
                ReqLogInitDto.builder()
                        .qryProcessRequest(qryProcessRequest)
                        .oldPointRunStatus(oldPointRunStatus)
                        .newPointRunStatus(newPointRunStatus)
                        .oldPointId(oldPointId)
                        .newPointId(newPointId)
                        .oldPointName(oldPointName)
                        .newPointName(newPointName)
                        .oldBContext(oldBContext)
                        .newBContext(newBContext)
                        .build());
    }


    /**
     * 中断流程节点
     * @param bContext 上下文
     * @param curPointId 当前节点id
     */
    @Transactional
    public void interrupt(BContext bContext, String curPointId) {
        if (null == bContext.getBizStatusEnum() || BizStatusEnum.PROCESSING.equals(bContext.getBizStatusEnum())) {
            bContext.setBizStatusEnum(BizStatusEnum.FAILED);
        }
        doFinishPoint(bContext, curPointId, CommonConstances.TEXT_INTERRUPT);
    }

    /**
     * 异常节点恢复正常
     *
     * @param bContext 上下文
     */
    @Transactional
    public void processingErrorPoint(BContext bContext) {
        String requestId = bContext.getRequestId();

        TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);
        if (null == qryProcessRequest) {
            log.error("流程请求记录不存在!");
            throw new BProcessException(BErrorCodeEnum.SYS_PROCESS_REQUEST_NONE_ERROR);
        }
        //状态恢复正常
        processRequestRepository.updateErrorToProcessing(qryProcessRequest.getRequestId());

        String oldPointRunStatus = qryProcessRequest.getPointRunStatus();
        String oldPointId = qryProcessRequest.getPointId();
        String oldPointName = qryProcessRequest.getPointName();
        String oldBContext = qryProcessRequest.getRunContext();

        String newPointRunStatus = PointRunStatusEnum.PROCESSING.getCode();
        String newPointId = qryProcessRequest.getPointId();
        String newPointName = qryProcessRequest.getPointName();
        String newBContext = qryProcessRequest.getRunContext();

        processRequestLogRepository.initTbProcessRequestLog(
                ReqLogInitDto.builder()
                        .qryProcessRequest(qryProcessRequest)
                        .oldPointRunStatus(oldPointRunStatus)
                        .newPointRunStatus(newPointRunStatus)
                        .oldPointId(oldPointId)
                        .newPointId(newPointId)
                        .oldPointName(oldPointName)
                        .newPointName(newPointName)
                        .oldBContext(oldBContext)
                        .newBContext(newBContext)
                        .build());
    }


}
